package cn.ahaoweb.drule.core;

import cn.ahaoweb.drule.core.util.DruleUtil;
import lombok.ToString;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @param <I> 入参类型
 * @param <O> 出参类型
 * @Name: 事实信息
 * @Description: 表示一次规则执行过程中产生的信息（已发生的信息）
 * @Author: wangzhonghao
 * @Date: 2025/2/13 15:49
 */
@ToString
public class FactInfo<I, O> {

    // 入参对象
    private I input;

    // 入参对象类型
    private Class<? extends I> inputClass;

    // 入参对象名
    private String inputName;

    // 出参对象
    private O output;

    // 出参对象类型
    private Class<? extends O> outputClass;

    // 出参对象名
    private String outputName;

    // 命中的规则列表
    private List<String> firedRules;

    // 是否出现异常，表示执行过程中出现异常
    private Boolean error;

    // 附加信息
    private Map<String, Object> attachInfo;

    private FactInfo() {
    }

    /**
     * 添加命中的规则
     *
     * @param ruleName
     */
    public void addFiredRule(String ruleName) {
        if (this.firedRules == null) {
            this.firedRules = new ArrayList<>();
        }
        if (!this.firedRules.contains(ruleName)) {
            this.firedRules.add(ruleName);
        }
    }

    public I getInput() {
        return input;
    }

    public void setInput(I input) {
        this.input = input;
    }

    public O getOutput() {
        return output;
    }

    public void setOutput(O output) {
        this.output = output;
    }

    public Class getInputClass() {
        return inputClass;
    }

    public Class getOutputClass() {
        return outputClass;
    }

    public List<String> getFiredRules() {
        return firedRules;
    }

    public void setFiredRules(List<String> firedRules) {
        this.firedRules = firedRules;
    }

    public void setInputClass(Class<? extends I> inputClass) {
        this.inputClass = inputClass;
    }

    public String getInputName() {
        return inputName;
    }

    public void setInputName(String inputName) {
        this.inputName = inputName;
    }

    public void setOutputClass(Class<? extends O> outputClass) {
        this.outputClass = outputClass;
    }

    public String getOutputName() {
        return outputName;
    }

    public void setOutputName(String outputName) {
        this.outputName = outputName;
    }

    public void attach(String key, Object value){
        if (this.attachInfo == null){
            this.attachInfo = new HashMap<>();
        }
        this.attachInfo.put(key, value);
    }

    public Map<String, Object> getAttachInfo() {
        return attachInfo;
    }

    public void setAttachInfo(Map<String, Object> attachInfo) {
        this.attachInfo = attachInfo;
    }

    public Boolean getError() {
        return error;
    }

    public void setError(Boolean error) {
        this.error = error;
    }

    public O getOutputInstance() {
        O o = null;
        if (output != null) {
            o = output;
            outputClass = (Class<? extends O>) output.getClass();
        } else if (outputClass != null) {
            try {
                output = outputClass.newInstance();
                o = output;
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        if (outputName == null) {
            outputName = DruleUtil.getLowerFirstClassName(outputClass);
        }
        return o;
    }

    /**
     * 是否执行失败
     * @return
     */
    public boolean isError(){
        return Boolean.TRUE.equals(error);
    }


    public static final class FactInfoBuilder<I, O> {
        private FactInfo<I, O> factInfo;

        private FactInfoBuilder() {
            factInfo = new FactInfo();
        }

        public static FactInfoBuilder builder() {
            return new FactInfoBuilder();
        }

        public FactInfoBuilder input(I input) {
            factInfo.setInput(input);
            return this;
        }

        /**
         * 自动处理入参类型和名称
         *
         * @param input
         * @return
         */
        public FactInfoBuilder byInput(I input) {
            factInfo.setInput(input);
            if (input != null) {
                factInfo.setInputClass((Class<? extends I>) input.getClass());
                factInfo.setInputName(DruleUtil.getLowerFirstClassName(factInfo.getInputClass()));
            }
            return this;
        }

        public FactInfoBuilder inputClass(Class<? extends I> inputClass) {
            factInfo.setInputClass(inputClass);
            return this;
        }

        public FactInfoBuilder byInputClass(Class<? extends I> inputClass) {
            factInfo.setInputClass(inputClass);
            factInfo.setInputName(DruleUtil.getLowerFirstClassName(inputClass));
            return this;
        }

        public FactInfoBuilder inputName(String inputName) {
            factInfo.setInputName(inputName);
            return this;
        }

        public FactInfoBuilder output(O output) {
            factInfo.setOutput(output);
            return this;
        }

        /**
         * 自动处理出参类型和名称
         *
         * @param output
         * @return
         */
        public FactInfoBuilder byOutput(O output) {
            factInfo.setOutput(output);
            if (output != null) {
                factInfo.setOutputClass((Class<? extends O>) output.getClass());
                factInfo.setOutputName(DruleUtil.getLowerFirstClassName(factInfo.getOutputClass()));
            }
            return this;
        }

        public FactInfoBuilder outputClass(Class<? extends O> outputClass) {
            factInfo.setOutputClass(outputClass);
            return this;
        }

        public FactInfoBuilder byOutputClass(Class<? extends O> outputClass) {
            factInfo.setOutputClass(outputClass);
            factInfo.setOutputName(DruleUtil.getLowerFirstClassName(outputClass));
            return this;
        }

        public FactInfoBuilder outputName(String outputName) {
            factInfo.setOutputName(outputName);
            return this;
        }

        public FactInfoBuilder attach(String key, Object value) {
            factInfo.attach(key,value);
            return this;
        }

        public FactInfoBuilder attachInfo(Map<String, Object> map) {
            factInfo.setAttachInfo(map);
            return this;
        }

        public FactInfo build() {
            return factInfo;
        }
    }

}
